package ltd.trackan.messagecenter.email;

import lombok.extern.slf4j.Slf4j;
import ltd.trackan.messagecenter.GreenLoong.GreenLoong;
import ltd.trackan.messagecenter.common.ValueCommon;
import ltd.trackan.messagecenter.email.formatted.CKDetection;
import ltd.trackan.messagecenter.email.formatted.JDAssets;
import ltd.trackan.messagecenter.email.formatted.JDFarmGet;
import ltd.trackan.messagecenter.model.coommon.RestBean;
import ltd.trackan.messagecenter.model.coommon.StatusCode;
import ltd.trackan.messagecenter.service.JDAccountInfoService;
import ltd.trackan.messagecenter.service.JDCertificateService;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import java.io.*;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 邮件控制
 *
 * @author wxa
 */
@Component
@Slf4j
public class MailController {
    @Resource
    JDAccountInfoService jdAccountInfoService;
    @Resource
    JDCertificateService jdCertificateService;
    @Resource
    GreenLoong greenLoong;
    
    // 指定主题邮件进行读取
    HashSet<String> hashSet = new HashSet<>();
    
    {
        hashSet.add("京东资产统计");
        hashSet.add("CK检测");
    }
    
    /*
     * 连接邮件
     */
    public void receiveMail(){
        // 1.连接邮件服务器的参数配置
        Properties props = new Properties();
        // 设置类型
        props.setProperty("mail.store.protocol", "pop3");
        // 设置端口（如果要使用ssl会报错）
        props.setProperty("mail.pop3.port", "110");
        // 设置收件人的pop3服务器
        props.setProperty("mail.pop3.host", "pop.126.com");
        // 2.创建定义整个应用程序所需的环境信息的Session对象
        Session session = Session.getInstance(props);
        Store store= null;
        Folder folder = null;
        try {
            // 3.通过session获取接收邮件的实例对象
            store = session.getStore("pop3");
            // 连接收件人的pop3服务器 --可能会超时
            // store.connect("w2799604016@163.com", "WEUAQNRWTYDUTVVK");
            store.connect("trackan@126.com", "ABAHJXUADDEAOFTX");
            // 获得用户的邮件账户，注意获取某个邮件夹的名称只能为inbox
            folder = store.getFolder("INBOX");
            // 设置对邮件的访问权限(可读可写)
            folder.open(Folder.READ_WRITE);
            // 4.得到邮件账户的所有邮件信息
            Message[] messages = folder.getMessages();
            // // 由于POP3协议无法获知邮件的状态,所以getUnreadMessageCount得到的是收件箱的邮件总数
            // System.out.println("邮件数: " + folder.getUnreadMessageCount());
            parseMessage(messages);
            // 5.得到收件箱中的所有邮件并且删除邮件
            // deleteMessage(messages);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6.关闭邮件文件夹对象和连接对象
            if (folder != null) {
                try {
                    folder.close(true);
                }catch (Exception e){
                    log.error(e.getMessage());
                }
            }
            if (store != null) {
                try {
                    store.close();
                }catch (Exception e){
                    log.error(e.getMessage());
                }
            }
        }
    }
    
    /**
     * 解析邮件
     *
     * @param messages 要解析的邮件列表
     */
    public void parseMessage(Message... messages) throws MessagingException, IOException {
        if (messages == null || messages.length < 1) {
            // System.out.println("未找到要解析的邮件!");
            return;
        }
        // 解析所有邮件
        for (Message message : messages) {
            MimeMessage msg = (MimeMessage) message;
            // System.out.println("------------------解析第" + msg.getMessageNumber() +
            // "封邮件-------------------- ");
            // System.out.println("主题: " + getSubject(msg));
            // System.out.println("发件人: " + getFrom(msg));
            // System.out.println("收件人：" + getReceiveAddress(msg, null));
            // System.out.println("发送时间：" + getSentDate(msg, null));
            // System.out.println("是否已读：" + isSeen(msg));
            // System.out.println("邮件优先级：" + getPriority(msg));
            // System.out.println("是否需要回执：" + isReplySign(msg));
            // System.out.println("邮件大小：" + msg.getSize() * 1024 + "kb");
            // boolean isContainerAttachment = isContainAttachment(msg);
            // System.out.println("是否包含附件：" + isContainerAttachment);
            // if (isContainerAttachment) {
            // saveAttachment(msg, "f:\\java\\mail\\file\\" + msg.getSubject() + "_" + i +
            // "_"); // 保存附件
            // }
            // 获得主题
            String subject = getSubject(msg);
            // 是固定主题
            if (hashSet.contains(subject)) {
                // 内容读取
                StringBuffer content = new StringBuffer(30);
                getMailTextContent(msg, content);
                // 格式后的内容存放变量
                HashMap<String, String> formatContent;
                if ("京东资产统计".equals(subject)) {
                    JDAssets assets = new JDAssets(content.toString());
                    formatContent = assets.getHashMap();
                    jdAccountInfoService.updateAccountInfo(formatContent);
                    ValueCommon.SEND_INFO_MAP.put("京东资产统计", formatContent);
                } else if ("CK检测".equals(subject)) {
                    CKDetection examine = new CKDetection(content.toString());
                    formatContent = examine.getHashMap();
                    // 修改数据库信息
                    Set<String> keySet = formatContent.keySet();
                    System.out.println("CK检测,失效的cookie" + Arrays.toString(keySet.toArray()));
                    RestBean<String> stringRestBean = jdCertificateService.updateJDCertificateState(keySet, 0);
                    if (stringRestBean.getCode() != StatusCode.SUCCESS.getCode()){
                        log.error(stringRestBean.getMsg());
                    }
                    ValueCommon.SEND_INFO_MAP.put("cookie已失效", formatContent);
                }
            }
            // 正则匹配是否是通知旧东东农场有水果可以收获
            if (Pattern.matches("东东农场-任务 - 账号.*", subject)) {
                // 内容读取
                StringBuffer content = new StringBuffer(30);
                getMailTextContent(msg, content);
                // 发送
                JDFarmGet farm = new JDFarmGet(content.toString());
                ValueCommon.SEND_INFO_MAP.put("旧版东东农场-提醒", farm.getHashMap());
            }
            // 正则匹配是否是通知新东东农场有水果可以收获
            if (Pattern.matches("新农场任务 - 账号.*", subject)) {
                // 内容读取
                StringBuffer content = new StringBuffer(30);
                getMailTextContent(msg, content);
                // 发送
                JDFarmGet farm = new JDFarmGet(content.toString());
                ValueCommon.SEND_INFO_MAP.put("新版东东农场-提醒", farm.getHashMap());
            }
            // 正则匹配是否是cookie失效
            if (Pattern.matches(".*已失效.*", subject)) {
                log.info("cookie已失效->"+subject);
                // 需要运行CK检查来禁用cookie
                if (greenLoong.login() == null) {
                    greenLoong.getTask();
                    greenLoong.runTask("CK检测");
                }else {
                    log.error("注意青龙面板登录失败，无法执行程序");
                }
                
            }
            deleteMessage(msg);
        }
        
    }
    
    /**
     * 解析并删除邮件
     *
     * @param messages 要解析的邮件列表
     */
    public void deleteMessage(Message... messages) throws MessagingException, IOException {
        if (messages == null || messages.length < 1) {
            throw new MessagingException("未找到要解析的邮件!");
        }
        /*
          邮件删除
         */
        for (Message message : messages) {
            String subject = message.getSubject();
            message.setFlag(Flags.Flag.DELETED, true);
            System.out.println("删除邮件: " + subject);
        }
    }
    
    /**
     * 获得邮件主题
     *
     * @param msg 邮件内容
     * @return 解码后的邮件主题
     */
    public String getSubject(MimeMessage msg) throws UnsupportedEncodingException, MessagingException {
        return MimeUtility.decodeText(msg.getSubject());
    }
    
    /**
     * 获得邮件发件人
     *
     * @param msg 邮件内容
     * @return 姓名 <Email地址>
     */
    public String getFrom(MimeMessage msg) throws MessagingException, UnsupportedEncodingException {
        String from = "";
        Address[] froms = msg.getFrom();
        if (froms.length < 1) {
            throw new MessagingException("没有发件人!");
        }
        
        InternetAddress address = (InternetAddress) froms[0];
        String person = address.getPersonal();
        if (person != null) {
            person = MimeUtility.decodeText(person) + " ";
        } else {
            person = "";
        }
        from = person + "<" + address.getAddress() + ">";
        
        return from;
    }
    
    /**
     * 根据收件人类型，获取邮件收件人、抄送和密送地址。如果收件人类型为空，则获得所有的收件人
     * <p>
     * Message.RecipientType.TO 收件人
     * </p>
     * <p>
     * Message.RecipientType.CC 抄送
     * </p>
     * <p>
     * Message.RecipientType.BCC 密送
     * </p>
     *
     * @param msg  邮件内容
     * @param type 收件人类型
     * @return 收件人1 <邮件地址1>, 收件人2 <邮件地址2>, ...
     */
    public String getReceiveAddress(MimeMessage msg, Message.RecipientType type) throws MessagingException {
        StringBuilder receiveAddress = new StringBuilder();
        Address[] addresss = null;
        if (type == null) {
            addresss = msg.getAllRecipients();
        } else {
            addresss = msg.getRecipients(type);
        }
        
        if (addresss == null || addresss.length < 1) {
            throw new MessagingException("没有收件人!");
        }
        for (Address address : addresss) {
            InternetAddress internetAddress = (InternetAddress) address;
            receiveAddress.append(internetAddress.toUnicodeString()).append(",");
        }
        // 删除最后一个逗号
        receiveAddress.deleteCharAt(receiveAddress.length() - 1);
        
        return receiveAddress.toString();
    }
    
    /**
     * 获得邮件发送时间
     *
     * @param msg 邮件内容
     * @return yyyy年mm月dd日 星期X HH:mm
     * @throws MessagingException
     */
    public String getSentDate(MimeMessage msg, String pattern) throws MessagingException {
        Date receivedDate = msg.getSentDate();
        if (receivedDate == null) {
            return "";
        }
        
        if (pattern == null || "".equals(pattern)) {
            pattern = "yyyy年MM月dd日 E HH:mm ";
        }
        
        return new SimpleDateFormat(pattern).format(receivedDate);
    }
    
    /**
     * 判断邮件中是否包含附件
     *
     * @param part 邮件内容
     * @return 邮件中存在附件返回true，不存在返回false
     * @throws MessagingException
     * @throws IOException
     */
    public boolean isContainAttachment(Part part) throws MessagingException, IOException {
        boolean flag = false;
        if (part.isMimeType("multipart/*")) {
            MimeMultipart multipart = (MimeMultipart) part.getContent();
            int partCount = multipart.getCount();
            for (int i = 0; i < partCount; i++) {
                BodyPart bodyPart = multipart.getBodyPart(i);
                String disp = bodyPart.getDisposition();
                if (disp != null && (disp.equalsIgnoreCase(Part.ATTACHMENT) || disp.equalsIgnoreCase(Part.INLINE))) {
                    flag = true;
                } else if (bodyPart.isMimeType("multipart/*")) {
                    flag = isContainAttachment(bodyPart);
                } else {
                    String contentType = bodyPart.getContentType();
                    if (contentType.contains("application")) {
                        flag = true;
                    }
                    
                    if (contentType.contains("name")) {
                        flag = true;
                    }
                }
                
                if (flag) {
                    break;
                }
            }
        } else if (part.isMimeType("message/rfc822")) {
            flag = isContainAttachment((Part) part.getContent());
        }
        return flag;
    }
    
    /**
     * 判断邮件是否已读
     *
     * @param msg 邮件内容
     * @return 如果邮件已读返回true, 否则返回false
     * @throws MessagingException
     */
    public boolean isSeen(MimeMessage msg) throws MessagingException {
        return msg.getFlags().contains(Flags.Flag.SEEN);
    }
    
    /**
     * 判断邮件是否需要阅读回执
     *
     * @param msg 邮件内容
     * @return 需要回执返回true, 否则返回false
     * @throws MessagingException
     */
    public boolean isReplySign(MimeMessage msg) throws MessagingException {
        boolean replySign = false;
        String[] headers = msg.getHeader("Disposition-Notification-To");
        if (headers != null) {
            replySign = true;
        }
        return replySign;
    }
    
    /**
     * 获得邮件的优先级
     *
     * @param msg 邮件内容
     * @return 1(High):紧急 3:普通(Normal) 5:低(Low)
     * @throws MessagingException
     */
    public String getPriority(MimeMessage msg) throws MessagingException {
        String priority = "普通";
        String[] headers = msg.getHeader("X-Priority");
        if (headers != null) {
            String headerPriority = headers[0];
            if (headerPriority.contains("1") || headerPriority.contains("High")) {
                priority = "紧急";
            } else if (headerPriority.contains("5") || headerPriority.contains("Low")) {
                priority = "低";
            } else {
                priority = "普通";
            }
        }
        return priority;
    }
    
    /**
     * 获得邮件文本内容
     *
     * @param part    邮件体
     * @param content 存储邮件文本内容的字符串
     */
    public void getMailTextContent(Part part, StringBuffer content) throws MessagingException, IOException {
        // 如果是文本类型的附件，通过getContent方法可以取到文本内容，但这不是我们需要的结果，所以在这里要做判断
        boolean isContainTextAttach = part.getContentType().indexOf("name") > 0;
        if (part.isMimeType("text/*") && !isContainTextAttach) {
            content.append(part.getContent().toString());
        } else if (part.isMimeType("message/rfc822")) {
            getMailTextContent((Part) part.getContent(), content);
        } else if (part.isMimeType("multipart/*")) {
            Multipart multipart = (Multipart) part.getContent();
            int partCount = multipart.getCount();
            for (int i = 0; i < partCount; i++) {
                BodyPart bodyPart = multipart.getBodyPart(i);
                getMailTextContent(bodyPart, content);
            }
        }
    }
    
    /**
     * 保存附件
     *
     * @param part    邮件中多个组合体中的其中一个组合体
     * @param destDir 附件保存目录
     */
    public void saveAttachment(Part part, String destDir)
            throws UnsupportedEncodingException, MessagingException,
            FileNotFoundException, IOException {
        if (part.isMimeType("multipart/*")) {
            Multipart multipart = (Multipart) part.getContent(); // 复杂体邮件
            // 复杂体邮件包含多个邮件体
            int partCount = multipart.getCount();
            for (int i = 0; i < partCount; i++) {
                // 获得复杂体邮件中其中一个邮件体
                BodyPart bodyPart = multipart.getBodyPart(i);
                // 某一个邮件体也有可能是由多个邮件体组成的复杂体
                String disp = bodyPart.getDisposition();
                if (disp != null && (disp.equalsIgnoreCase(Part.ATTACHMENT) || disp.equalsIgnoreCase(Part.INLINE))) {
                    InputStream is = bodyPart.getInputStream();
                    saveFile(is, destDir, decodeText(bodyPart.getFileName()));
                } else if (bodyPart.isMimeType("multipart/*")) {
                    saveAttachment(bodyPart, destDir);
                } else {
                    String contentType = bodyPart.getContentType();
                    if (contentType.contains("name") || contentType.contains("application")) {
                        saveFile(bodyPart.getInputStream(), destDir, decodeText(bodyPart.getFileName()));
                    }
                }
            }
        } else if (part.isMimeType("message/rfc822")) {
            saveAttachment((Part) part.getContent(), destDir);
        }
    }
    
    /**
     * 读取输入流中的数据保存至指定目录
     *
     * @param is       输入流
     * @param fileName 文件名
     * @param destDir  文件存储目录
     */
    private void saveFile(InputStream is, String destDir, String fileName)
            throws FileNotFoundException, IOException {
        BufferedInputStream bis = new BufferedInputStream(is);
        BufferedOutputStream bos = new BufferedOutputStream(
                Files.newOutputStream(new File(destDir + fileName).toPath()));
        int len = -1;
        while ((len = bis.read()) != -1) {
            bos.write(len);
            bos.flush();
        }
        bos.close();
        bis.close();
    }
    
    /**
     * 文本解码
     *
     * @param encodeText 解码MimeUtility.encodeText(String text)方法编码后的文本
     * @return 解码后的文本
     */
    public String decodeText(String encodeText) throws UnsupportedEncodingException {
        if (encodeText == null || encodeText.isEmpty()) {
            return "";
        } else {
            return MimeUtility.decodeText(encodeText);
        }
    }
}
